package com.miyake.dsp.otdr.ui;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSplitPane;

import com.miyake.dsp.DspComposite.DspCompositeHandler;
import com.miyake.dsp.converter.AmplifierSetting;
import com.miyake.dsp.converter.DigitizerSetting;
import com.miyake.dsp.converter.LimiterSetting;
import com.miyake.dsp.converter.LogSetting;
import com.miyake.dsp.converter.MovingAverageSetting;
import com.miyake.dsp.converter.OffsetSetting;
import com.miyake.dsp.filter.FilterSetting;
import com.miyake.dsp.filter.FilterSetting.FilterAlgo;
import com.miyake.dsp.filter.FilterSetting.FilterType;
import com.miyake.dsp.generator.NoiseSetting;
import com.miyake.dsp.generator.PulseSetting;
import com.miyake.dsp.otdr.OtdrSetting;
import com.miyake.dsp.otdr.SpliceDetector;
import com.miyake.dsp.otdr.SpliceDetectorSetting;
import com.miyake.dsp.ui.DspComboBox;
import com.miyake.dsp.ui.DspCompositeUI;
import com.miyake.dsp.ui.DspCompositeUiFactory;
import com.miyake.dsp.ui.DspEditor;
import com.miyake.dsp.ui.DspTextField;
import com.miyake.dsp.ui.DspWidget.DspWidgetHandler;
import com.miyake.dsp.ui.LineChartPanel;

public class OtdrSimulator extends JFrame {

	/**
	 * 
	 */
	private static final long serialVersionUID = -1339904966844374677L;
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		new OtdrSimulator().setVisible(true);
	}

	private OtdrSetting otdrSetting;
	private PulseSetting pulseSetting;
	private DspCompositeUI entryPoint;
	private NoiseSetting noiseSetting;
	
	private DspCompositeUI otdr;
	private FilterSetting filterSetting;
	private double defaultNoise = 0.000001;
	private DspEditor editor;
	private MovingAverageSetting smoothingSetting;
	private LimiterSetting limiterSetting;
	private MeasureThread thread;
	private OffsetSetting offsetSetting;
	private AmplifierSetting ampSetting;
	private DigitizerSetting digitizerSetting;
	private MovingAverageSetting smoothingSetting2;
	
	private double defaultFrequency;
	private SpliceDetectorSetting spliceDetectorSetting;
	
	public OtdrSimulator() {
		setSize(800, 600);
		
		initialize();
	}

	private void initialize() {
		defaultFrequency = 1000e6;
		
		// Creates settings
		otdrSetting = new OtdrSetting(1550, 20);
		pulseSetting = new PulseSetting(100e-9/3, 1, 0);
		noiseSetting = new NoiseSetting(defaultNoise);
		filterSetting = new FilterSetting(FilterAlgo.Chebyshev2, FilterType.Lowpass, 1, defaultFrequency/5);
		filterSetting.setDelta1(0.1);
		filterSetting.setDelta2(0.4);
		smoothingSetting = new MovingAverageSetting(1);
		limiterSetting = new LimiterSetting(0, 0.01);
		offsetSetting = new OffsetSetting(0);
		ampSetting = new AmplifierSetting(1);
		digitizerSetting = new DigitizerSetting(0.001);
		smoothingSetting2 = new MovingAverageSetting(10);
		spliceDetectorSetting = new SpliceDetectorSetting(35, 0.001);
		// Creates a processor factory
		DspCompositeUiFactory factory = new DspCompositeUiFactory(defaultFrequency);
		
		// Creates processors
		DspCompositeUI pulse = factory.createPulseGenerator(pulseSetting);
		otdr = factory.createOTDR(otdrSetting);
		DspCompositeUI conv = factory.createConvolution(otdr);
		DspCompositeUI noise = factory.createNoiseGenerator(noiseSetting);
		DspCompositeUI filter = factory.createFilter(filterSetting );
		DspCompositeUI limiter = factory.createLimiter(limiterSetting);
		DspCompositeUI offset = factory.createOffset(offsetSetting);
		DspCompositeUI smoothing = factory.createMovingAverage(smoothingSetting);
		final DspCompositeUI log = factory.createLog(new LogSetting(5, -80));
		DspCompositeUI diff = factory.createDiff();
		final DspCompositeUI absolute = factory.createAbsolute();
		DspCompositeUI diff2 = factory.createDiff();
		DspCompositeUI amp = factory.createAmplifier(ampSetting);
		final DspCompositeUI smoothing2 = factory.createMovingAverage(smoothingSetting2);
		DspCompositeUI smoothing3 = factory.createMovingAverage(smoothingSetting2);
		DspCompositeUI smoothing4 = factory.createMovingAverage(smoothingSetting2);
		DspCompositeUI filter2 = factory.createFilter(new FilterSetting(FilterAlgo.Fir, FilterType.Lowpass, 2, defaultFrequency/5));
		final DspCompositeUI spliceDetector = factory.createSpliceDetector(spliceDetectorSetting);
		
		final DspCompositeUI digitizer = factory.createDigitizer(digitizerSetting);
		//// Creates processing sequence
		pulse.add(conv);
		conv.add(amp);
		amp.add(offset);
		offset.add(noise);
		noise.add(filter);
		filter.add(limiter);	
		limiter.add(smoothing);
		smoothing.add(log);
				
		log.add(smoothing2);
		
//		smoothing2.add(smoothing3);
//		smoothing3.add(smoothing4);		
//		smoothing4.add(diff);
		smoothing2.add(diff);
		
		diff.add(absolute);
		absolute.add(spliceDetector);
		
		// UI initialization
		entryPoint = pulse;
		
		Container c = this.getContentPane();
		c.setLayout(new BorderLayout());
		
		JSplitPane pane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
		c.add(pane);
		
		JPanel p = new JPanel();
		p.setLayout(new FlowLayout());
		final Component setting = createSettingPanel();
		setting.setPreferredSize(new Dimension(800, 40));
		p.add(setting);
		final Component otdrArea = otdr.getPanel();
		otdrArea.setPreferredSize(new Dimension(800,800));
		p.add(otdrArea);
		pane.add(p, JSplitPane.TOP);
		
		//double xcoeff = otdrSetting.getDistanceRange()/otdrSetting.getTrace().length;
		final double xcoeff = 1.0 / defaultFrequency * 3e8 * 1.5; // m
		log.getChart().setxCoeff(xcoeff);
		pane.add(log.getChart(), JSplitPane.BOTTOM);

		diff.getChart().setxCoeff(xcoeff);
		diff2.getChart().setxCoeff(xcoeff);
		absolute.getChart().setxCoeff(xcoeff);
		digitizer.getChart().setxCoeff(xcoeff);
		spliceDetector.getComposite().addHandler(new DspCompositeHandler() {
			@Override
			public void onRun() {
//				List<Integer> pos = ((SpliceDetector)spliceDetector.getComposite().getProcessor()).getEvents();
				List<Double> markers = new ArrayList<Double>();
				double[] data = spliceDetector.getComposite().getLatestData().getRealPart();
				for (int i = 0; i < data.length; i++) {
					if (data[i] > 0) {
						markers.add(i * xcoeff);
					}
				}
				log.getChart().addMarker(markers);
			}	
		});

		diff.getComposite().addHandler(new DspCompositeHandler() {
			@Override
			public void onRun() {
				spliceDetectorSetting.setTrace(smoothing2.getComposite().getLatestData().getRealPart());
			}	
		});
		
		editor = new DspEditor(defaultFrequency, false);
		editor.setEntryPoint(entryPoint);

		pulse.run();

		// Window component event listener
		this.addComponentListener(new ComponentListener() {

			@Override
			public void componentHidden(ComponentEvent arg0) {
				// TODO Auto-generated method stub
				
			}

			@Override
			public void componentMoved(ComponentEvent arg0) {
				// TODO Auto-generated method stub
				
			}

			@Override
			public void componentResized(ComponentEvent arg0) {
			//	setting.setBounds(0, 0, arg0.getComponent().getWidth(), 80);
			//	otdrArea.setBounds(0, 0, arg0.getComponent().getWidth(), arg0.getComponent().getHeight()-setting.getHeight());
			}

			@Override
			public void componentShown(ComponentEvent arg0) {
				// TODO Auto-generated method stub
				
			}
			
		});
	}

	private Component createSettingPanel() {
		JPanel panel = new JPanel();

		DspTextField text = new DspTextField("Pulse");
		text.setValue(String.valueOf(pulseSetting.getWidth()*1e9*2*1.5));		
		text.addHandler(new DspWidgetHandler() {
			@Override
			public void onChange(String value) {
				double width = Double.valueOf(value)/1e9/2/1.5;
				smoothingSetting2.setLength((int)(width * defaultFrequency));
				pulseSetting.setWidth(width);
				entryPoint.run();
			}
		});	
		
		DspTextField range = new DspTextField("Range");
		range.setValue(String.valueOf(otdrSetting.getDistanceRange()));	
		range.addHandler(new DspWidgetHandler() {
			@Override
			public void onChange(String value) {
				otdrSetting.setDistanceRange(Double.valueOf(value));
				otdr.run();
				entryPoint.run();
			}
		});			
	
		List<String> list = new ArrayList<String>();
		for (int i = 0; i < 20; i++) {
			list.add(String.valueOf(Math.pow(2, i)));
		}
		DspComboBox average = new DspComboBox("Average", list);
		average.setSelected("1");
		average.addHandler(new DspWidgetHandler() {
			@Override
			public void onChange(String value) {
				setAverage(Double.valueOf(value));
			}
		});
		
		List<String> attList = new ArrayList<String>();
		for (int i = 0; i < 25; i += 3) {
			attList.add(String.valueOf(i));
		}
		DspComboBox attenuator = new DspComboBox("Attenuator", attList);
		attenuator.setSelected("0");
		attenuator.addHandler(new DspWidgetHandler() {
			@Override
			public void onChange(String value) {
				setAttenuator(Double.valueOf(value));
			}
		});
		
		DspTextField smoothing = new DspTextField("Smoothing");
		smoothing.setValue(String.valueOf(smoothingSetting.getLength()));	
		smoothing.addHandler(new DspWidgetHandler() {
			@Override
			public void onChange(String value) {
				smoothingSetting.setLength(Integer.valueOf(value));
				entryPoint.run();
			}
		});		
		
		JButton button = new JButton("Block");
		button.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				editor.setVisible(true);
			}
		});

		
		final JButton start = new JButton("Start");
		start.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				double average = 512;
				for (int i = 0;i < 10; i++) {
					setAverage(average);
					average = average + 512;
					try {
						Thread.sleep(1);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				/*
				if (thread == null || !thread.isAlive()) {
					thread = new MeasureThread();
					thread.start();
					start.setText("Stop");
				}
				else {
					thread.setStop(true);
					start.setText("Start");
				}
				*/
			}
		});
		
		panel.add(text);
		panel.add(range);
		panel.add(average);
		panel.add(smoothing);
		panel.add(attenuator);
		panel.add(button);
		panel.add(start);
		
		return panel;
	}
	
	private void setAttenuator(double value) {
		double mag = 1/Math.pow(10, value/5);
		ampSetting.setAmplitude(mag);
		entryPoint.run();
	}

	private void setAverage(double value) {
		noiseSetting.setMagnitude(defaultNoise / Math.sqrt(value));
		entryPoint.run();
	}

	class MeasureThread extends Thread {
		private boolean stop = false;
		
		public boolean isStop() {
			return stop;
		}

		public void setStop(boolean stop) {
			this.stop = stop;
		}

		@Override
		public synchronized void start() {
			stop = false;
			super.start();
		}

		@Override
		public void run() {
			double average = 512;
			while(!stop) {
				setAverage(average);
				average = average + 512;
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
		
	}
	
	
}
